x86: On CPU online/offline from dom0, try flushing RCU work on EBUSY.
authorKeir Fraser <keir@xen.org>
Fri, 14 Jan 2011 14:19:55 +0000 (14:19 +0000)
committerKeir Fraser <keir@xen.org>
Fri, 14 Jan 2011 14:19:55 +0000 (14:19 +0000)
Although the caller should react appropriately to EBUSY, if the error
is due to pending RCU work then we can help things along by executing
rcu_barrier() and then retrying. To this end, this changeset is an
optimisation only.

Signed-off-by: Keir Fraser <keir@xen.org>
xen/arch/x86/platform_hypercall.c
xen/arch/x86/sysctl.c

index 812df802287356ef5c3dce3f6814ef165e913231..2733fc3b9e689d458563d27d9d382d655511f6ff 100644 (file)
@@ -55,11 +55,9 @@ static long cpu_frequency_change_helper(void *data)
     return cpu_frequency_change(this_cpu(freq));
 }
 
-static long cpu_down_helper(void *data)
-{
-    int cpu = (unsigned long)data;
-    return cpu_down(cpu);
-}
+/* from sysctl.c */
+long cpu_up_helper(void *data);
+long cpu_down_helper(void *data);
 
 ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
 {
@@ -443,40 +441,43 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
 
     case XENPF_cpu_online:
     {
-        int cpu;
+        int cpu = op->u.cpu_ol.cpuid;
 
-        cpu = op->u.cpu_ol.cpuid;
-        if (!cpu_present(cpu))
+        if ( !cpu_present(cpu) )
         {
             ret = -EINVAL;
             break;
         }
-        else if (cpu_online(cpu))
+
+        if ( cpu_online(cpu) )
         {
             ret = 0;
             break;
         }
 
-        ret = cpu_up(cpu);
+        ret = continue_hypercall_on_cpu(
+            0, cpu_up_helper, (void *)(unsigned long)cpu);
         break;
     }
 
     case XENPF_cpu_offline:
     {
-        int cpu;
+        int cpu = op->u.cpu_ol.cpuid;
 
-        cpu = op->u.cpu_ol.cpuid;
-        if (!cpu_present(cpu))
+        if ( !cpu_present(cpu) )
         {
             ret = -EINVAL;
             break;
-        } else if (!cpu_online(cpu))
+        }
+
+        if ( !cpu_online(cpu) )
         {
             ret = 0;
             break;
         }
+
         ret = continue_hypercall_on_cpu(
-          0, cpu_down_helper, (void *)(unsigned long)cpu);
+            0, cpu_down_helper, (void *)(unsigned long)cpu);
         break;
     }
     break;
index 7bd20b9d0ddcef2ccd09cdcea9202169228e9a8c..042bde4c4622f28727419309154309056f315710 100644 (file)
 
 #define get_xen_guest_handle(val, hnd)  do { val = (hnd).p; } while (0)
 
-static long cpu_down_helper(void *data)
+long cpu_up_helper(void *data)
 {
     int cpu = (unsigned long)data;
-    return cpu_down(cpu);
+    int ret = cpu_up(cpu);
+    if ( ret == -EBUSY )
+    {
+        /* On EBUSY, flush RCU work and have one more go. */
+        rcu_barrier();
+        ret = cpu_up(cpu);
+    }
+    return ret;
+}
+
+long cpu_down_helper(void *data)
+{
+    int cpu = (unsigned long)data;
+    int ret = cpu_down(cpu);
+    if ( ret == -EBUSY )
+    {
+        /* On EBUSY, flush RCU work and have one more go. */
+        rcu_barrier();
+        ret = cpu_down(cpu);
+    }
+    return ret;
 }
 
 extern int __node_distance(int a, int b);
@@ -170,7 +190,8 @@ long arch_do_sysctl(
         switch ( sysctl->u.cpu_hotplug.op )
         {
         case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
-            ret = cpu_up(cpu);
+            ret = continue_hypercall_on_cpu(
+                0, cpu_up_helper, (void *)(unsigned long)cpu);
             break;
         case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
             ret = continue_hypercall_on_cpu(